import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Bidirectional
from tensorflow.keras.models import load_model
from sklearn.metrics import mean_squared_error, r2_score
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import matplotlib.dates as mdates
from datetime import datetime, timedelta

# 设置中文字体
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 强制使用交互式后端
plt.switch_backend('TkAgg')  # 或 'Qt5Agg'，取决于您的环境

# 加载数据
df = pd.read_excel('神经网络数据.xlsx')

# 提取特征和目标变量
X = df[['箱温', 'O2', 'CO', 'CO2', ]].values
y = df['煤温'].values

# 数据标准化
scaler_X = StandardScaler()
scaler_y = StandardScaler()
X_scaled = scaler_X.fit_transform(X)
y_scaled = scaler_y.fit_transform(y.reshape(-1, 1))

# 将数据重塑为 LSTM 所需的形状 [samples, timesteps, features]
X_scaled = X_scaled.reshape((X_scaled.shape[0], 1, X_scaled.shape[1]))

# 重新构建模型
model = Sequential()
model.add(Bidirectional(LSTM(50, return_sequences=True), input_shape=(1, X.shape[1])))
model.add(Bidirectional(LSTM(50)))
model.add(Dense(1))

# 编译模型
model.compile(optimizer='adam', loss='mean_squared_error')

try:
    # 加载模型权重
    model.load_weights('coal_temperature_prediction_model.h5')
    print("模型权重加载成功！")
except Exception as e:
    print(f"加载权重时出错: {e}")

# 使用模型进行预测，用全部数据
y_pred_scaled = model.predict(X_scaled)

# 将预测结果反标准化
y_pred = scaler_y.inverse_transform(y_pred_scaled)

# 将真实值反标准化
y_original = scaler_y.inverse_transform(y_scaled)

# 计算评估指标
mse = mean_squared_error(y_original, y_pred)
r2 = r2_score(y_original, y_pred)
print(f'Mean Squared Error: {mse}')
print(f'R - squared: {r2}')

# 创建日期序列（模拟数据采集时间）
start_date = datetime.now()
dates = [start_date - timedelta(hours=i) for i in range(len(y_original) - 1, -1, -1)]

# 定义显示窗口大小
window_size = 50  # 每次显示的点数

# 创建画布和子图
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10), gridspec_kw={'height_ratios': [3, 1]})
fig.suptitle('煤温预测实时监控系统', fontsize=16)

# 初始化数据
x_data, y_actual_data, y_pred_data = [], [], []
error_data = []

# 创建线条对象
line_actual, = ax1.plot([], [], 'b-', label='实际煤温', linewidth=2)
line_pred, = ax1.plot([], [], 'r-', label='预测煤温', linewidth=2)
line_error, = ax2.plot([], [], 'g-', label='预测误差', linewidth=1.5)

# 创建文本对象用于显示统计信息
stats_text = ax1.text(0.02, 0.85, '', transform=ax1.transAxes, bbox=dict(facecolor='white', alpha=0.8))
current_error_text = ax2.text(0.02, 0.80, '', transform=ax2.transAxes, bbox=dict(facecolor='white', alpha=0.8))

# 设置坐标轴标签和标题
ax1.set_ylabel('温度 (°C)')
ax1.set_title('实际与预测煤温对比')
ax1.grid(True, linestyle='--', alpha=0.7)

ax2.set_xlabel('样本索引')  # 修改为样本索引
ax2.set_ylabel('误差 (°C)')
ax2.set_title('预测误差分析')
ax2.grid(True, linestyle='--', alpha=0.7)

# 调整图例位置
ax1.legend(loc='lower left', bbox_to_anchor=(0, 1.02), ncol=2, borderaxespad=0)


# 初始化函数
def init():
    line_actual.set_data([], [])
    line_pred.set_data([], [])
    line_error.set_data([], [])
    stats_text.set_text('')
    current_error_text.set_text('')
    return line_actual, line_pred, line_error, stats_text, current_error_text


# 动画更新函数
def update(frame):
    # 添加新数据点
    x_data.append(frame)  # 使用样本索引代替日期
    y_actual_data.append(y_original[frame][0])
    y_pred_data.append(y_pred[frame][0])
    error_data.append(y_original[frame][0] - y_pred[frame][0])

    # 动态滑动窗口
    if frame >= window_size:
        # 只显示最近的window_size个数据点
        x_display = x_data[-window_size:]
        y_actual_display = y_actual_data[-window_size:]
        y_pred_display = y_pred_data[-window_size:]
        error_display = error_data[-window_size:]
    else:
        # 数据点不足window_size时，显示全部数据
        x_display = x_data
        y_actual_display = y_actual_data
        y_pred_display = y_pred_data
        error_display = error_data

    # 更新线条数据
    line_actual.set_data(x_display, y_actual_display)
    line_pred.set_data(x_display, y_pred_display)
    line_error.set_data(x_display, error_display)

    # 更新坐标轴范围
    ax1.set_xlim(x_display[0], x_display[-1])
    ax1.set_ylim(min(y_actual_display + y_pred_display) - 1, max(y_actual_display + y_pred_display) + 1)

    ax2.set_xlim(x_display[0], x_display[-1])
    ax2.set_ylim(min(error_display) - 0.5, max(error_display) + 0.5)

    # 计算当前评估指标（使用全部数据）
    if len(y_actual_data) >= 2:  # 确保有足够的数据点计算R²
        current_mse = mean_squared_error(y_actual_data, y_pred_data)
        current_r2 = r2_score(y_actual_data, y_pred_data)
        stats_text.set_text(f'当前样本: {frame + 1}/{len(y_original)}\nMSE: {current_mse:.4f}\nR²: {current_r2:.4f}')
    else:
        stats_text.set_text(f'当前样本: {frame + 1}/{len(y_original)}\nMSE: 计算中...\nR²: 计算中...')

    current_error_text.set_text(f'当前误差: {error_data[-1]:.2f}°C')

    return line_actual, line_pred, line_error, stats_text, current_error_text


# 创建动画
ani = FuncAnimation(fig, update, frames=len(y_original), init_func=init,
                    interval=100, blit=True, repeat=False)

# 显示图形
plt.tight_layout()
plt.subplots_adjust(hspace=0.5)  # 增加子图间距
plt.show()

# 保存预测结果
predictions_df = pd.DataFrame({
    '样本索引': range(len(y_original)),
    '实际煤温': y_original.flatten(),
    '预测煤温': y_pred.flatten(),
    '预测误差': (y_original - y_pred).flatten()
})
predictions_df.to_excel('coal_temperature_predictions_loaded_model.xlsx', index=False)